package com.jdzy.student.service.impl;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jdzy.model.common.vos.ResultA;
import com.jdzy.model.common.vos.ResultCodeEnum;
import com.jdzy.model.student.dtos.PlanQueryDto;
import com.jdzy.model.student.pojos.*;
import com.jdzy.model.student.vos.PlanExcelVo;
import com.jdzy.model.student.vos.PlanSubmitVo;
import com.jdzy.student.config.MailConfig;
import com.jdzy.student.mapper.PlanMapper;
import com.jdzy.student.mapper.SignRecordPlanMapper;
import com.jdzy.student.mapper.StudentBaseInformationMapper;
import com.jdzy.student.mapper.StudentMapper;
import com.jdzy.student.service.PlanService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class PlanServiceImpl extends ServiceImpl<PlanMapper, Plan> implements PlanService {

   private final  PlanMapper planMapper;
   private final SignRecordPlanMapper signRecordPlanMapper;
   private final StudentMapper studentMapper;
   private final JavaMailSender javaMailSender;
   private final StudentBaseInformationMapper studentBaseInformationMapper;

   private final MailConfig mailConfig;




    @Value("${spring.mail.username}")
    private String from;

    /**
     * 根据学生id，计划类型，计划时间 查询学生计划
     * @param stuId
     * @param planType
     * @param startTime
     * @param endTime
     * @return
     */
   @Override
    public ResultA<List<PlanQueryDto>> findPlansByStudentIdAndTypeAndTime(String stuId, PlanType planType, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss ")Date startTime, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss ")Date endTime) {
//1.根据学生id，计划类型，计划时间 查询学生计划 使用LambdaQueryWrapper
// 使用 LambdaQueryWrapper 构建查询条件
//        LambdaQueryWrapper<Plan> queryWrapper = Wrappers.lambdaQuery();
//        queryWrapper.eq(Plan::getStuId, stuId)
//                .eq(Plan::getPlanType, planType.ordinal()) // 枚举类型转换为对应的数据库整数值
//                .between(startTime.before(endTime) ? Plan::getStartTime : Plan::getEndTime, startTime, endTime) // 确保时间范围有效
//                .or(wrapper -> wrapper
//                        .between(Plan::getStartTime, startTime, endTime)
//                        .or().between(Plan::getEndTime, startTime, endTime)); // 包含跨时间段的计划
// 注意：上面的时间范围查询逻辑可能需要根据实际需求调整，特别是当 startTime 和 endTime 相等或非常接近时
// 另外，上面的逻辑在 startTime 在 endTime 之后时可能会产生不符合预期的结果，这里仅作为示例
// 在实际应用中，你可能需要更精确地处理时间范围查询，比如只查询完全在指定时间范围内的计划

// 为了简化，这里我们假设只查询完全在指定时间范围内的计划，不包括跨时间段的计划
// 因此，我们可以将上面的查询条件简化为：
//
//        queryWrapper.eq(Plan::getStuId, stuId)
//                    .eq(Plan::getPlanType, planType.ordinal())
//                    .ge(Plan::getStartTime, startTime) // 大于等于开始时间
//                    .le(Plan::getEndTime, endTime);   // 小于等于结束时间

// 检查时间范围的有效性（可选）
        if (startTime.after(endTime)) {
            throw new IllegalArgumentException(String.valueOf(ResultCodeEnum.DATA_ERROR));
        }

        // 执行查询
        List<Plan> plans = planMapper.findPlansByStudentIdAndTypeAndTime(stuId, planType, startTime, endTime);

       // 将 Plan 转换为 PlanQueryDto
        List<PlanQueryDto> planQueryDtos = plans.stream().map(plan -> {
            PlanQueryDto dto = new PlanQueryDto();
            dto.setId(plan.getId());
            dto.setStuId(plan.getStuId());
            dto.setPlanType(plan.getPlanType());
            dto.setPlanName(plan.getPlanName());
            dto.setContent(plan.getContent());
            dto.setStartTime(plan.getStartTime());
            dto.setEndTime(plan.getEndTime());
            return dto;
        }).collect(Collectors.toList());

        // 使用 ResultA 封装返回结果
        return ResultA.ok(planQueryDtos); // 返回转换后的计划和成功状态码
    }

    /**
     * 保存计划信息
     * @param planSubmitVo
     */
    @Override
    public void saveOrUpdatePlan(PlanSubmitVo planSubmitVo) {
        Long PlanId = planSubmitVo.getId();
        boolean isUpdate = PlanId != null&&!PlanId.equals(0L);//判断是否是更新操作
        //1. 保存或更新Plan自身的信息（insert,update）
        super.saveOrUpdate(planSubmitVo);
        //2.如果是更新，需要删除之前的内容
        if (isUpdate) {
            super.removeById(PlanId);
        }
    }

    /**
     * 签到
     * @param stuId
     * @param planId
     * @return
     */
    @Override
    public boolean signPlan(String stuId, Long planId) {

        // 1.  检查学生是否存在
        LambdaQueryWrapper<Student> studentQueryWrapper = new LambdaQueryWrapper<>();
        studentQueryWrapper.eq(Student::getStuId, stuId);
        boolean studentExists = studentMapper.selectCount(studentQueryWrapper) > 0;

        // 2. 检查计划是否存在
        LambdaQueryWrapper<Plan> planQueryWrapper = new LambdaQueryWrapper<>();
        planQueryWrapper.eq(Plan::getId, planId);
        boolean planExists = planMapper.selectCount(planQueryWrapper) > 0;

        // 3. 检查学生或计划是否不存在
        if (!studentExists || !planExists) {
            return false; // 学生或计划不存在，返回失败
        }


        // 4.检查是否已经签到过
        boolean alreadySigned = signRecordPlanMapper.existsByStuIdAndPlanIdAndSignDate(stuId, planId, new Date());
        if (alreadySigned) {
            return false; // 已经签到，返回失败
        }

        // 5.创建签到记录并保存
        SignRecordPlan signRecordPlan = new SignRecordPlan();
        signRecordPlan.setStuId(stuId);
        signRecordPlan.setPlanId(planId);
        signRecordPlan.setSignDate(new Date());
        signRecordPlanMapper.save(signRecordPlan);
        return true; // 签到成功
    }

    /**
     * 获取学生全部的签到记录
     * @param stuId
     * @return
     */
    @Override
    public List<String> getSignDates(String stuId) {
        // 从数据库获取所有签到记录
        List<Date> signDates = signRecordPlanMapper.findSignDatesByStuId(stuId);

        // 创建日期时间格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        // 将 Date 转换为 LocalDateTime，并格式化为字符串
        return signDates.stream()
                .map(date -> LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).format(formatter))
                .collect(Collectors.toList());
    }

    /**
     * 导出数据
     * @param response
     */
    @SneakyThrows
    @Override
    public void exportData(HttpServletResponse response) {
        try {

            // 设置响应结果类型
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");

            // 这里URLEncoder.encode可以防止中文乱码 当然和easy excel没有关系
            String fileName = URLEncoder.encode("计划数据", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
//            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

            // 查询数据库中的数据
            List<Plan> planList = planMapper.selectAll();
            List<PlanExcelVo> planExcelVoList = new ArrayList<>(planList.size());

            // 将从数据库中查询到的Plan对象转换成PlanExcelVo对象
            for(Plan plan : planList) {
                PlanExcelVo planExcelVo = new PlanExcelVo();
                BeanUtils.copyProperties(plan, planExcelVo, PlanExcelVo.class);
//                //日期格式化
//                // 创建日期时间格式化器
//                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//                // 将 Date 转换为 LocalDateTime，并格式化为字符串
//                planExcelVo.setStartTime(LocalDateTime.ofInstant(plan.getStartTime().toInstant(), ZoneId.systemDefault()).format(formatter));
//                planExcelVoList.add(planExcelVo);


            }

            // 写出数据到浏览器端
            EasyExcel.write(response.getOutputStream(), PlanExcelVo.class).sheet("计划数据").doWrite(planExcelVoList);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送邮件
     */
    @Override
    @Scheduled(cron = "0 0 21 * * ?") // 每天21点（晚上9点触发）
    public void sendReminderEmails() {
// 获取当前日期
        LocalDate today = LocalDate.now();

        // 查询所有学生信息（包括邮箱）
        List<StudentBaseInformation> students = studentBaseInformationMapper.selectList(null);

        // 遍历学生列表
        students.forEach(student -> {
            String studentMail = student.getMail();
            if (studentMail != null && !studentMail.isEmpty()) {
                // 查询学生今天的签到记录
                LambdaQueryWrapper<SignRecordPlan> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(SignRecordPlan::getStuId, student.getId())
                        .ge(SignRecordPlan::getSignDate, today.atStartOfDay())
                        .le(SignRecordPlan::getSignDate, today.atTime(LocalTime.MAX));
                List<SignRecordPlan> signRecords = signRecordPlanMapper.selectList(queryWrapper);

                // 如果没有签到记录，则发送邮件
                if (signRecords.isEmpty()) {
                    sendReminderMail(studentMail, today);
                }
            }
        });
    }

    private void sendReminderMail(String studentEmail, LocalDate today) {
        // 构建邮件内容
        String subject = mailConfig.getSubject();
        String contentPrefix = mailConfig.getContentPrefix();
        String contentSuffix = mailConfig.getContentSuffix();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String formattedDate = today.format(formatter);
        String content = contentPrefix + formattedDate + contentSuffix;

        // 创建邮件消息
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(from);
        mailMessage.setTo(studentEmail);
        mailMessage.setSubject(subject);
        mailMessage.setText(content);

        // 发送邮件
        javaMailSender.send(mailMessage);
//        log.info("Reminder email sent to: {}", studentEmail);
    }
}
